package com.wlz.nio;

import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @author wlz
 * @date 2022-07-06  9:41 下午
 */
@Slf4j
public class MyNioSelectorServer {

    public static void main(String[] args) throws Exception{

        // 创建 nio ServerSocketChannel
        ServerSocketChannel serverSocket = ServerSocketChannel.open();

        serverSocket.socket().bind(new InetSocketAddress(9000));

        // 设置 ServerSocketChannel 为非阻塞
        serverSocket.configureBlocking(false);

        // 打开 Selector 处理 channel ,即 创建  epoll
        Selector selector = Selector.open();

        // 把 ServerSocketChannel 注册 到 Selector 上， 并且 selector 对客户端accept 连接操作感兴趣
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);

        log.info("服务端启动成功 。。。。。");


        while (true) {

            // 阻塞 等待 需要处理的事件 发生
            selector.select();

            // 获取 selector 中注册 的全部 事件的 SelectionKey 实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            // 遍历 SelectionKey 对事件进行处理
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()) {

                SelectionKey key = iterator.next();

                // 如果 是 OP_ACCEPT 事件，则进行 连接获取 和事件注册
                if (key.isAcceptable()) {
                    ServerSocketChannel  server = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    log.info("客户端 连接成功 。。。。。");
                }
                // 如果是 OP_READ 事件， 则进行读取和打印
                else if (key.isReadable()) {
                    SocketChannel socketChannel  = (SocketChannel) key.channel();
                    ByteBuffer allocate = ByteBuffer.allocate(128);
                    int len = socketChannel.read(allocate);
                    if (len > 0) {
                        log.info("接收到的消息: {}", new String(allocate.array()));
                    }else if (len == -1) { // 如果客户端断开连接，关闭 socket
                        log.info("客户端断开连接 。。。。");
                        socketChannel.close();
                    }
                }
                // 从事件 集合里删除 本次处理的key， 防止下次 select 重复处理
                iterator.remove();
            }

        }

    }
}
